package net.didactylos.helpline.servlet;

import java.io.*;
import java.util.List;
import java.util.Properties;

import javax.mail.*;
import javax.mail.Message;
import javax.mail.internet.*;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.mail.search.*;

import net.didactylos.helpline.data.*;
import net.sourceforge.stripes.util.Log;

public class EmailManager
{

	private static final Log log = Log.getInstance(EmailManager.class);
	private static Properties emailProperties;
	
	private static Queue defaultQueue;
	
	private org.hibernate.Session session;
	
	public EmailManager(org.hibernate.Session session)
	{
		super();
		this.session = session;
		
		if (defaultQueue == null)
		{
			defaultQueue = (Queue) session
			.createQuery("from Queue as queue where queue.name = ?")
			.setString(0, "Email")
			.uniqueResult();
			
			if (defaultQueue == null)
			{
				defaultQueue = new Queue();
				defaultQueue.setName("Email");
				session.save(defaultQueue);
				
				Queue approve = new Queue();
				approve.setName("Check");
				session.save(approve);
			}
		}
	}

	public Queue getDefaultQueue()
	{
		return defaultQueue;
	}

	public static Session getSession()
	{
		if (emailProperties == null)
		{
			emailProperties = new Properties();
			ClassLoader loader = EmailManager.class.getClassLoader();
			InputStream istream = loader.getResourceAsStream("Helpline.properties");
			try
			{
				emailProperties.load(istream);
			}
			catch (IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		String host = emailProperties.getProperty("helpline.smtp.host");
		
		// Create empty properties
		Properties props = new Properties();
		props.put("mail.smtp.host", host);

		// Get session
		Session session = Session.getDefaultInstance(props, null);
		return session;
	}
	
	private static Store getStore() throws NoSuchProviderException,
	MessagingException
	{
		Session session = getSession();
		
		String host = emailProperties.getProperty("helpline.imap.host");
		String username = emailProperties.getProperty("helpline.username");
		String password = emailProperties.getProperty("helpline.password");

		// Get the store
		Store store = session.getStore("imap");
		store.connect(host, username, password);
		return store;
	}
	
	public void checkInbox(List<net.didactylos.helpline.data.Message> messages, boolean checkSeen) throws MessagingException
	{
		ContactManager cMgr = new ContactManager(session);
		ConversationManager convMgr = new ConversationManager(session);
		Store store = getStore();

		// Get folder
		Folder folder = store.getFolder("INBOX");
		folder.open(Folder.READ_WRITE);

		// Get directory
		Message message[];
		
		if (checkSeen)
		{
			message = folder.getMessages();
		}
		else
		{
			message = folder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
		}

		for (int i = 0; i < message.length; i++)
		{
			MimeMessage mime = (MimeMessage) message[i];
			net.didactylos.helpline.data.Message m = new net.didactylos.helpline.data.Message();
			
			String messageId = mime.getMessageID();
			if (messageId.length() > 100)
			{
				MimeMessage newMsg = new MimeMessage((MimeMessage) mime);
				newMsg.setHeader("Message-ID", "<" + java.util.UUID.randomUUID().toString() + ">");
				newMsg.saveChanges();
				folder.appendMessages(new Message[] {newMsg});
				mime.setFlag(Flags.Flag.DELETED, true);
				
				log.debug("Message-ID too long. (Copied to new email)");
				continue;
			}
			m.setMessageId(messageId);
			m.setSubject(mime.getSubject());
			m.setCreateTime(mime.getReceivedDate());
			
			InternetAddress from = (InternetAddress) mime.getFrom()[0];
			if (from == null)
			{
				log.debug("no From");
				from = (InternetAddress) mime.getReplyTo()[0];
			}
			if (from == null)
			{
				log.debug("no ReplyTo");
				break;
			}
			Contact contact = cMgr.findContact(from);
			m.setAuthor(contact);
			
			convMgr.append(m);
			
			String body = getBody(mime);
			if (body == null)
			{
				// Log problem!
				body = "Could not read message";
				// Mark read anyway (or it will be read twice)
				mime.setFlag(Flags.Flag.SEEN, true);
			}
			m.setMessageBody(body);
			m.setType(MessageType.INBOUND);
			m.setQueue(defaultQueue);
			messages.add(m);
			
			session.save(m);
		}

		// Close connection 
		folder.close(true);
		store.close();
	}
	
	public static String getBody(Part m)
	{
		try
		{
			if (m.isMimeType("text/plain"))
			{
				return (String) m.getContent();
			}
			if (m.isMimeType("text/html"))
			{
				// TODO: Sanitize
				return (String) m.getContent();
			}
			if (m.isMimeType("multipart/*"))
			{
				Multipart mp = (Multipart) m.getContent();
				for (int i = 0; i < mp.getCount(); i++)
				{
					BodyPart bp = mp.getBodyPart(i);
					String body = getBody(bp);
					if (body != null)
					{
						return body;
					}
				}
			}
		}
		catch (MessagingException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}
	
	public static void sendEmail(net.didactylos.helpline.data.Message message)
	{
		InternetAddress from;
		Template t = message.getTemplate();
		String m = message.getFormattedBody();

		try
		{
			from = new InternetAddress(t.getFromAddress(),
					t.getFromName(), "UTF-8");
		}
		catch (UnsupportedEncodingException e)
		{
			e.printStackTrace();
			return;
		}

		Session session = getSession();

		try
		{
			// Get the store
			Store store = getStore();

			// Get folder
			Folder folder = store.getFolder("INBOX");
			folder.open(Folder.READ_WRITE);
			
			MimeMessage msg = getRealEmail(message.getInReplyTo(), folder);
			
			// create a message
			MimeMessage reply;
			if (msg == null)
			{
				reply = new MimeMessage(session);
				//reply.addRecipient(arg0, arg1);
			}
			else
			{
				reply = (MimeMessage) msg.reply(false);
				
				// If the contact has been amended, then the reply will be going
				// to the wrong person. Fix it:
				
				InternetAddress to = (InternetAddress) reply.getAllRecipients()[0];
				EmailContact replyTo = message.getInReplyTo().getAuthor();
				
				if (! to.getAddress().equals(replyTo.getEmailAddress()))
				{
					reply.setRecipient(RecipientType.TO, replyTo.getInternetAddress());
				}
				
			}
			reply.setFrom(from);
			reply.setText(m, "UTF-8");

			Transport.send(reply);
			
			folder.close(false);
			store.close();
		}
		catch (MessagingException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private static MimeMessage getRealEmail(
			net.didactylos.helpline.data.Message message, Folder folder)
			throws MessagingException
	{
		MimeMessage msg = null;

		Message messages[] = folder.search(
				new MessageIDTerm(message.getMessageId()));
		
		if (messages.length > 0)
		{
			msg = (MimeMessage) messages[0];
		}
		return msg;
	}
	
	public static void moveToSpam(net.didactylos.helpline.data.Message message) throws MessagingException
	{
		Store store = getStore();
		
		Folder inbox = store.getFolder("INBOX");
		inbox.open(Folder.READ_WRITE);
		
		MimeMessage email = getRealEmail(message, inbox);
		
		// Get folder
		Folder spam = store.getFolder("SPAM");
		if (!spam.exists())
		{
			spam.create(Folder.HOLDS_MESSAGES);
		}
		
		inbox.copyMessages(new Message[] {email}, spam);
		email.setFlag(Flags.Flag.DELETED, true);
		
		// Expunge and close folder.
		inbox.close(true);

		store.close();
	}
	
	public static void forwardEmail(net.didactylos.helpline.data.Message message, Template template, EmailContact contact) throws MessagingException
	{
		Session session = getSession();
		
		// Get the store
		Store store = getStore();

		// Get folder
		Folder inbox = store.getFolder("INBOX");
		inbox.open(Folder.READ_WRITE);
		
		MimeMessage msg = getRealEmail(message, inbox);
		
		// Create the message to forward
		Message forward = new MimeMessage(session);

		// Fill in header
		forward.setSubject("Fwd: " + msg.getSubject());
		try
		{
			forward.setFrom(new InternetAddress(template.getFromAddress(), template.getFromName(), "UTF-8"));
		}
		catch (UnsupportedEncodingException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}
		forward.addRecipient(Message.RecipientType.TO, 
		  contact.getInternetAddress());

		// Create your new message part
		BodyPart messageBodyPart = new MimeBodyPart();
		messageBodyPart.setText(
				"This message was reassigned to you:\n\n" +

				"-----Original Message-----\n" +
				"From: " + msg.getFrom()[0] + "\n" +
				"Sent: " + msg.getSentDate() + "\n" +
				"To: " + msg.getRecipients(RecipientType.TO)[0] + "\n" +
				"Subject: " + msg.getSubject() + "\n\n"
		);

		// Create a multi-part to combine the parts
		Multipart multipart = new MimeMultipart();
		multipart.addBodyPart(messageBodyPart);

		// Create and fill part for the forwarded content
		messageBodyPart = new MimeBodyPart();
		messageBodyPart.setDataHandler(msg.getDataHandler());

		// Add part to multi part
		multipart.addBodyPart(messageBodyPart);

		// Associate multi-part with message
		forward.setContent(multipart);
		
		// We don't want replies going back to the service.
		forward.setReplyTo(msg.getFrom());

		// Send message
		Transport.send(forward);
		
		inbox.close(false);

		store.close();
	}
}
